// This is the main DLL file.

#include "stdafx.h"
#include "MAPIConcubine.h"


#define MAPI_NATIVE_BODY 0x00010000

#define MAPI_NATIVE_BODY_TYPE_RTF 0x00000001
#define MAPI_NATIVE_BODY_TYPE_HTML 0x00000002
#define MAPI_NATIVE_BODY_TYPE_PLAINTEXT 0x00000004

typedef struct {
	ULONG       size;
	ULONG       ulFlags;
	ULONG       ulInCodePage;
	ULONG       ulOutCodePage;
} RTF_WCSINFO;

typedef struct {
	ULONG       size;   
	ULONG       ulStreamFlags;
} RTF_WCSRETINFO;

//These are definitions for the WrapCompressedRTFStreamEx function.
typedef HRESULT (STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAMEX) (
	LPSTREAM lpCompressedRTFStream, CONST RTF_WCSINFO * pWCSInfo, LPSTREAM * lppUncompressedRTFStream, RTF_WCSRETINFO * pRetInfo);
typedef WRAPCOMPRESSEDRTFSTREAMEX *LPWRAPCOMPRESSEDRTFSTREAMEX;


namespace MAPIConcubine {

	///
	/// construction code - we can initialize the MAPI Session here
	/// 
	Fabric::Fabric ()
	{
		MAPIInitialize(0);
	}


	/// 
	/// desctructor - cleanup the MAPI session here.
	/// 
	Fabric::~Fabric ()
	{
		MAPIUninitialize();
	}

	/// 
	/// Returns the MAPI profilename of the current session.
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the Outlook Application.Session object.
	/// [returns] - the name of the currently used profile.
	String^ Fabric::GetProfileName(Object^ mapiObject)
	{

		// the result returned to the calling method
		String^ result = nullptr;

		// pointer to IUnknown interface
		IUnknown* pUnknown = 0;

		// pointer to the MAPI session interface
		LPMAPISESSION lpMAPISession = 0;

		// pointer to a profilesection interface
		LPPROFSECT lpProfileSection = 0;

		// pointer to a structure that receives the result from HrGetOneProp
		LPSPropValue lpSPropValue = 0;

		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr)
			throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");

		try
		{
			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMAPISession interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMAPISession, (void**)&lpMAPISession) != S_OK)
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_IMAPISession");

			// use the OpenProfileSection of the MAPISession object to retrieve a pointer to the current profilesection interface
			if( lpMAPISession->OpenProfileSection((LPMAPIUID)GLOBAL_PROFILE_SECTION_MAPIUID ,NULL,STGM_READ, &lpProfileSection) != S_OK)
				throw gcnew Exception("OpenProfileSection method failed!");

			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(lpProfileSection,PR_PROFILE_NAME_W,&lpSPropValue) != S_OK)
				throw gcnew Exception("HrGetOneProp failed for property PR_PROFILE_NAME_W !");

			// create a managed string from the unmanaged string.
			return gcnew String(  lpSPropValue->Value.lpszW ); 

		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// and translate it into a human readable message
			if (dw != 0) throw gcnew Exception(GetErrorText(dw),ex);

			throw ex;
		}
		finally
		{
			// Free buffer allocated by MAPI
			if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);

			// cleanup all references to MAPI Objects
			if (lpProfileSection!=0) lpProfileSection->Release();
			if (lpMAPISession!=0) lpMAPISession->Release();
			if (pUnknown!=0) pUnknown->Release();
		}
	}

	/// 
	/// Returns the current MAPI profilenames available in the system registry.
	/// 
	/// [returns] - a string array of all available profilenames.
	array<String^,1>^ Fabric::EnumProfileNames(){

		String^ registryPath = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles";
		RegistryKey^ key = nullptr;
		try
		{
			key = Registry::CurrentUser->OpenSubKey ( registryPath );
			return key->GetSubKeyNames ();
		}
		catch(System::Exception^ ex)
		{
			throw ex;
		}
		finally
		{
			key->Close();
		}
	}

	/// 
	/// Adds an InternetHeader to an outgoing Outlook- Email Message
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook mailItem object.
	/// [in] headerName - the name of the header to add.
	/// [in] headerValue - the value of the header to add.
	void Fabric::AddMessageHeader(Object^ mapiObject, String^ headerName, String^ headerValue){

		// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// a structure for the MANEDProp variable
		MAPINAMEID* pNamedProp = 0;

		// an array of PropertyTags as result for the GetIDdFromNames Method.
		SPropTagArray* lpTags = 0;

		// Convert the .Net Strings to unmanaged memory blocks				
		IntPtr pHeaderName = Marshal::StringToHGlobalUni (headerName);
		IntPtr pHeaderValue = Marshal::StringToHGlobalUni  (headerValue);

		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) 
				throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");


			// A well Google documented ID used to access the SMTP Headers of an Outlook Message
			GUID magicId = { 0x00020386, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; 

			// The pointer to the namedProp structure
			if (MAPIAllocateBuffer(sizeof(MAPINAMEID), (LPVOID*)&pNamedProp) != S_OK)  
				throw gcnew Exception("Could not allocate memory buffer.");

			// The ID is used to acces the named Property
			pNamedProp->lpguid = (LPGUID)&magicId; 

			// Set the PropertyName
			pNamedProp->ulKind = MNID_STRING;
			pNamedProp->Kind.lpwstrName = (LPWSTR) pHeaderName.ToPointer();

			// Call the GetIDsFromNames to retrieve the propertyID to use
			if(pMAPIProp->GetIDsFromNames(1, &pNamedProp,  MAPI_CREATE, &lpTags ) != S_OK)
				throw gcnew Exception(String::Format ("Error retrieving GetIDsFromNames: {0}.",headerName) );

			// A structure that is used to pass the value to the property
			SPropValue value;
			// The PROP_TAG macro creates the correct value for Type and PropertyID
			value.ulPropTag = PROP_TAG(PT_UNICODE, PROP_ID(lpTags->aulPropTag[0]));
			value.Value.LPSZ = (LPWSTR) pHeaderValue.ToPointer();

			if( HrSetOneProp(pMAPIProp, &value) != S_OK) 
				throw gcnew Exception(String::Format ("Error setting Header: {0}.",headerName) );

		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// and translate it into a human readable message
			if (dw != 0) throw gcnew Exception(GetErrorText(dw),ex);

			throw ex;
		}
		finally
		{

			if (pNamedProp != 0) MAPIFreeBuffer(pNamedProp);
			if (lpTags != 0) MAPIFreeBuffer(lpTags);

			// Free alocated unmanaged memory
			Marshal::FreeHGlobal (pHeaderName);
			Marshal::FreeHGlobal (pHeaderValue);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}
	}


	/// 
	/// Reads an InternetHeader from an Outlook- Email Message
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook mailItem object.
	/// [in] headerName - the name of the header to retrieve.
	/// [returns] headerValue - the value of the header with the given name.
	String^ Fabric::ReadMessageHeader(Object^ mapiObject, String^ headerName){
		// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// a structure for the MANEDProp variable
		MAPINAMEID* pNamedProp = 0;

		// an array of PropertyTags as result for the GetIDdFromNames Method.
		SPropTagArray* lpTags = 0;

		// pointer to a structure that receives the result from HrGetOneProp
		LPSPropValue lpSPropValue = 0;

		// Convert the .Net Strings to unmanaged memory blocks				
		IntPtr pHeaderName = Marshal::StringToHGlobalUni (headerName);

		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");


			// A well Google documented ID used to access the SMTP Headers of an Outlook Message
			GUID magicId = { 0x00020386, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; 

			// The pointer to the namedProp structure
			if (MAPIAllocateBuffer(sizeof(MAPINAMEID), (LPVOID*)&pNamedProp) != S_OK)  
				throw gcnew Exception("Could not allocate memory buffer.");

			// The ID is used to acces the named Property
			pNamedProp->lpguid = (LPGUID)&magicId; 

			// Set the PropertyName
			pNamedProp->ulKind = MNID_STRING;
			pNamedProp->Kind.lpwstrName = (LPWSTR) pHeaderName.ToPointer();

			// Call the GetIDsFromNames to retrieve the propertyID to use
			if(pMAPIProp->GetIDsFromNames(1, &pNamedProp,  STGM_READ, &lpTags ) != S_OK) 
				throw gcnew Exception(String::Format ("Error retrieving GetIDsFromNames: {0}.",headerName) );

			// The PROP_TAG macro creates the correct value for Type and PropertyID
			ULONG propTag = PROP_TAG(PT_UNICODE, PROP_ID(lpTags->aulPropTag[0]));

			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp,propTag,&lpSPropValue) != S_OK)
				throw gcnew Exception("HrGetOneProp failed for named property !");

			// create a managed string from the unmanaged string.
			return gcnew String(  lpSPropValue->Value.lpszW ); 

		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// and translate it into a human readable message
			if (dw != 0) throw gcnew Exception(GetErrorText(dw),ex);

			throw ex;
		}
		finally
		{
			// Free Buffer allocated from MAPI
			if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);
			if (pNamedProp != 0) MAPIFreeBuffer(pNamedProp);
			if (lpTags != 0) MAPIFreeBuffer(lpTags);

			// Free alocated unmanaged memory
			Marshal::FreeHGlobal (pHeaderName);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}
	}


	/// 
	/// Reads the complete SMTP headers from an Outlook- Email Message
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook mailItem object.
	/// [returns] the complete internet transport message headers.
	String^ Fabric::ReadMessageHeader(Object^ mapiObject){

		// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// pointer to a structure that receives the result from HrGetOneProp
		LPSPropValue lpSPropValue = 0;


		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");

			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp,PR_TRANSPORT_MESSAGE_HEADERS /*PR_BODY*/	,&lpSPropValue) != S_OK)
				throw gcnew Exception("HrGetOneProp failed for named property !");

			// create a managed string from the unmanaged string.
			return gcnew String(  lpSPropValue->Value.lpszW ); 

		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// if dw = 0 then there are no Headers for this message
			if (dw == 0) return String::Empty ;

			// and translate it into a human readable message
			throw gcnew Exception(GetErrorText(dw),ex);
		}
		finally
		{
			// Free Buffer allocated from MAPI
			if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}		


	}


	/// 
	/// Reads Body- Email Message
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook mailItem object.
	/// [returns] the complete internet transport message headers.
	String^ Fabric::ReadMessageBody(Object^ mapiObject){

		// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// pointer to a structure that receives the result from HrGetOneProp
		LPSPropValue lpSPropValue = 0;


		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if (!SUCCEEDED(pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage))) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");

			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp, PR_SENDER_EMAIL_ADDRESS	, &lpSPropValue) != S_OK)
				throw gcnew Exception("HrGetOneProp failed for named property !");
			String^ ret = "sender:" + gcnew String(  lpSPropValue->Value.lpszW ); 

			if (HrGetOneProp(pMAPIProp, PR_DISPLAY_TO, &lpSPropValue) == S_OK)

				ret += "receive:" + gcnew String(  lpSPropValue->Value.lpszW ); 

			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp, PR_BODY	,&lpSPropValue) != S_OK)
				throw gcnew Exception("HrGetOneProp failed for named property !");

			ret += ":" + gcnew String(  lpSPropValue->Value.lpszW ); 
			// create a managed string from the unmanaged string.
			return ret;

		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// if dw = 0 then there are no Headers for this message
			if (dw == 0) return String::Empty ;

			// and translate it into a human readable message
			throw gcnew Exception(GetErrorText(dw),ex);
		}
		finally
		{
			// Free Buffer allocated from MAPI
			if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}		


	}


	/// 
	/// Reads Body- Email Message
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook mailItem object.
	/// [returns] the complete internet transport message headers.
	Dictionary<String^, Object^>^ Fabric::ReadSecureData(Object^ mapiObject){

	// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// pointer to a structure that receives the result from HrGetOneProp
		LPSPropValue lpSPropValue = 0;


		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			Dictionary<String^, Object^>^ secureData = gcnew Dictionary<String^, Object^>;
			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");
			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp, PR_SENDER_EMAIL_ADDRESS	, &lpSPropValue) == S_OK)
				//throw gcnew Exception("HrGetOneProp failed for named property !");
				secureData->Add("from",  gcnew String(  lpSPropValue->Value.lpszW )); 

			if (HrGetOneProp(pMAPIProp, PR_SENDER_NAME	, &lpSPropValue) == S_OK)
				//throw gcnew Exception("HrGetOneProp failed for named property !");
				secureData->Add("senderName",  gcnew String(  lpSPropValue->Value.lpszW )); 

			if (HrGetOneProp(pMAPIProp, PR_DISPLAY_TO, &lpSPropValue) == S_OK)
				secureData->Add("to",  gcnew String(  lpSPropValue->Value.lpszW )); 

			if (HrGetOneProp(pMAPIProp, PR_DISPLAY_CC, &lpSPropValue) == S_OK)
				secureData->Add("cc",  gcnew String(  lpSPropValue->Value.lpszW ));

			if (HrGetOneProp(pMAPIProp, PR_DISPLAY_BCC, &lpSPropValue) == S_OK)	 
				secureData->Add("bcc",  gcnew String(  lpSPropValue->Value.lpszW )); 

			IMessage* lpMsg= pMessage;

			{
				HINSTANCE hDLL= LoadLibrary(TEXT("msmapi32.dll"));

				if (hDLL == NULL)
				{
					throw gcnew Exception("dll...");
				}

				// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.

				LPWRAPCOMPRESSEDRTFSTREAMEX pfnWrapEx;

				SYSTEM_INFO si;

				ZeroMemory(&si, sizeof(SYSTEM_INFO));

				pfnWrapEx= (LPWRAPCOMPRESSEDRTFSTREAMEX) GetProcAddress(hDLL,
					"WrapCompressedRTFStreamEx");
				if(!pfnWrapEx)
				{
					FreeLibrary(hDLL);
					throw gcnew Exception("Wrap ...");
				}


				HRESULT         hRes = S_OK;
				LPSTREAM        lpCompressed = NULL;
				LPSTREAM        lpUncompressed = NULL;
				
				 
				ULONG           ulRead = 0;
				RTF_WCSINFO     wcsinfo = {0};
				RTF_WCSRETINFO  retinfo = {0};
				LPSPropValue    lpPropCPID = NULL;

				retinfo.size = sizeof(RTF_WCSRETINFO);

				wcsinfo.size = sizeof(RTF_WCSINFO);
				wcsinfo.ulFlags = MAPI_NATIVE_BODY;
				wcsinfo.ulOutCodePage = 0;

				// Retrieve the value of the Internet code page.
				// Pass this value to the WrapCompressedRTFStreamEx function.
				// If the property is not found, the default is 0.
				/*if(SUCCEEDED(hRes = HrGetOneProp(lpMsg, PR_INTERNET_CPID , &lpPropCPID)))
				{
				wcsinfo.ulInCodePage = lpPropCPID->Value.l;
				}*/

				// Open the compressed RTF stream.
				if(SUCCEEDED(hRes = lpMsg->OpenProperty(PR_RTF_COMPRESSED,
					&IID_IStream,
					STGM_READ | STGM_DIRECT,
					0,
					(LPUNKNOWN*)&lpCompressed)))
				{

					// Notice that the WrapCompressedRTFStreamEx function has been loaded
					// by using the GetProcAddress function into pfnWrapEx.

					// Call the WrapCompressedRTFStreamEx function.
					//WrapCompressedRTFStreamEx
					if(SUCCEEDED(hRes = pfnWrapEx(lpCompressed,
						&wcsinfo,
						&lpUncompressed,
						&retinfo)))
					{

						//printf("Body's native type is: ");

						// Check what the native body type is.
						switch(retinfo.ulStreamFlags)
						{
						case MAPI_NATIVE_BODY_TYPE_RTF:
							//printf("MAPI_NATIVE_BODY_TYPE_RTF\n");
							break;
						case MAPI_NATIVE_BODY_TYPE_HTML:
							//printf("MAPI_NATIVE_BODY_TYPE_HTML\n");
							break;
						case MAPI_NATIVE_BODY_TYPE_PLAINTEXT:
							//printf("MAPI_NATIVE_BODY_TYPE_PLAINTEXT\n");
							break;
						default:
							;
							//printf("UNKNOWN\n");
						}

						
						// Read the first 1,000 characters out of the stream.
						 
						byte*            szBody = new byte[1024] ;
					
						String^ s = gcnew String("");
						while(SUCCEEDED(hRes = lpUncompressed->Read(szBody, 1024, &ulRead)))
						{
							//printf("First %d characters of the native body stream:\n%s\n", ulRead, szBody);
							
							if (ulRead <=0) break;
								array<Char>^ a = gcnew array<Char>(ulRead);
							for (int i=0; i<ulRead; i++) a[i] = szBody[i];
							 s += gcnew String(a);
							
							
						}
						secureData->Add("fbody", s); 
						delete szBody;
						
					}
				}

				MAPIFreeBuffer(lpPropCPID);
				if(lpUncompressed)lpUncompressed->Release();
				if(lpCompressed)lpCompressed->Release();

			}



			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp, PR_BODY	,&lpSPropValue) == S_OK)
				secureData->Add("body",  gcnew String(  lpSPropValue->Value.lpszW )); 

			//ret += ":" + gcnew String(  lpSPropValue->Value.lpszW ); 
			// create a managed string from the unmanaged string.
			return secureData;

		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// if dw = 0 then there are no Headers for this message
			//if (dw == 0) return String::Empty ;

			// and translate it into a human readable message
			throw gcnew Exception(GetErrorText(dw),ex);
		}
		finally
		{
			// Free Buffer allocated from MAPI
			if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}		


	}



	/// 
	/// Sets the AppointmentLabelColor for a Outlook- Appointment
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook appointmentItem object.
	/// [in] the desired colortype that you wish to set 
	void Fabric::SetAppointmentLabelColor(Object^ mapiObject, AppointmentLabelColor color){

		// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// a structure for the NAMEDProp variable
		MAPINAMEID* pNamedProp = 0;

		// an array of PropertyTags as result for the GetIDdFromNames Method.
		SPropTagArray* lpTags = 0;

		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) 
				throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");


			// A well Google documented ID used to access an appointment color label
			GUID magicId = { 0x00062002, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
			LONG propertyName = 0x8214;

			// The pointer to the namedProp structure
			if (MAPIAllocateBuffer(sizeof(MAPINAMEID), (LPVOID*)&pNamedProp) != S_OK)  
				throw gcnew Exception("Could not allocate memory buffer.");

			// The ID is used to acces the named Property
			pNamedProp->lpguid = (LPGUID)&magicId; 

			// Set the PropertyName - we have an ID as Property here
			pNamedProp->ulKind = MNID_ID;
			pNamedProp->Kind.lID = propertyName;

			// Call the GetIDsFromNames to retrieve the propertyID to use
			if(pMAPIProp->GetIDsFromNames(1, &pNamedProp,  MAPI_CREATE, &lpTags ) != S_OK)
				throw gcnew Exception(String::Format ("Error retrieving GetIDsFromNames: {0}.",propertyName) );

			// A structure that is used to pass the value to the property
			SPropValue value;

			// The PROP_TAG macro creates the correct value for Type and PropertyID
			value.ulPropTag = PROP_TAG(PT_LONG, PROP_ID(lpTags->aulPropTag[0]));
			value.Value.l = (LONG)color;

			if( HrSetOneProp(pMAPIProp, &value) != S_OK) 
				throw gcnew Exception(String::Format ("Error setting AppointmentLabelColor: {0}.",color) );

			// save the changes to the Item
			pMessage->SaveChanges (KEEP_OPEN_READWRITE);


		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// and translate it into a human readable message
			if (dw != 0) throw gcnew Exception(GetErrorText(dw),ex);

			throw ex;
		}
		finally
		{
			// free memory allocated from MAPI
			if (pNamedProp != 0) MAPIFreeBuffer(pNamedProp);
			if (lpTags != 0) MAPIFreeBuffer(lpTags);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}

	}

	/// 
	/// Sets the AppointmentLabelColor for a Outlook- Appointment
	/// 
	/// [in] mapiObject - the MAPIOBJECT property of the outlook appointmentItem object.
	/// [returns] the current label colortype of the item, default when the value is greater then 10 
	Fabric::AppointmentLabelColor Fabric::GetAppointmentLabelColor(Object^ mapiObject){

		// Pointer to IUnknown Interface
		IUnknown* pUnknown = 0;

		// Pointer to IMessage Interface
		IMessage* pMessage = 0;

		// Pointer to IMAPIProp Interface
		IMAPIProp* pMAPIProp = 0;

		// a structure for the MANEDProp variable
		MAPINAMEID* pNamedProp = 0;

		// an array of PropertyTags as result for the GetIDdFromNames Method.
		SPropTagArray* lpTags = 0;

		// pointer to a structure that receives the result from HrGetOneProp
		LPSPropValue lpSPropValue = 0;

		// if we have no MAPIObject everything is senseless...
		if (mapiObject == nullptr) throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");
		try
		{

			// retrive the IUnknon Interface from our MAPIObject comming from Outlook.
			pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();

			// try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
			if ( pUnknown->QueryInterface (IID_IMessage, (void**)&pMessage) != S_OK) 
				throw gcnew Exception("QueryInterface failed on IUnknown for IID_Message");

			// try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
			if ( pMessage->QueryInterface (IID_IMAPIProp, (void**)&pMAPIProp) != S_OK)
				throw gcnew Exception("QueryInterface failed on IMessage for IID_IMAPIProp");

			// double check, if we wave no pointer, exit...
			if (pMAPIProp == 0) throw gcnew Exception("Unknown Error on receiving the Pointer to MAPI Properties.");


			// A well Google documented ID used to access an appointment color label
			GUID magicId = { 0x00062002, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
			LONG propertyName = 0x8214;

			// The pointer to the namedProp structure
			if (MAPIAllocateBuffer(sizeof(MAPINAMEID), (LPVOID*)&pNamedProp) != S_OK)  
				throw gcnew Exception("Could not allocate memory buffer.");

			// The ID is used to acces the named Property
			pNamedProp->lpguid = (LPGUID)&magicId; 

			// Set the PropertyName - we have an ID as Property here
			pNamedProp->ulKind = MNID_ID;
			pNamedProp->Kind.lID = propertyName;

			// Call the GetIDsFromNames to retrieve the propertyID to use
			if(pMAPIProp->GetIDsFromNames(1, &pNamedProp,  STGM_READ, &lpTags ) != S_OK) 
				throw gcnew Exception(String::Format ("Error retrieving GetIDsFromNames: {0}.",propertyName) );

			// The PROP_TAG macro creates the correct value for Type and PropertyID
			ULONG propTag = PROP_TAG(PT_LONG, PROP_ID(lpTags->aulPropTag[0]));

			// use the HrGetOneProp method to retrieve the profile name property
			// the lpPropValue pointer points to the result value 
			if (HrGetOneProp(pMAPIProp,propTag,&lpSPropValue) != S_OK)
				throw gcnew Exception("HrGetOneProp failed for named property !");

			if (( lpSPropValue->Value.l > 0) && ( lpSPropValue->Value.l < 11))
				return (Fabric::AppointmentLabelColor ) lpSPropValue->Value.l;
			else
				return Fabric::AppointmentLabelColor::None ;
		}
		catch (Exception^ ex)
		{
			// if an error occures get the MAPI error code
			DWORD dw = GetLastError();

			// and translate it into a human readable message
			if (dw != 0) throw gcnew Exception(GetErrorText(dw),ex);

			throw ex;
		}
		finally
		{
			// Free Buffer allocated from MAPI
			if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);
			if (pNamedProp != 0) MAPIFreeBuffer(pNamedProp);
			if (lpTags != 0) MAPIFreeBuffer(lpTags);

			// cleanup all references to COM Objects
			if (pMAPIProp!=0) pMAPIProp->Release();
			if (pMessage!=0) pMessage->Release();
			if (pUnknown!=0) pUnknown->Release();
		}
	}



	/// 
	/// This method takes an MAPI error code as parameter (GetLastError()) and returns the error message as managed string.
	/// 
	/// [in] dw - the MAPI error code.
	/// [returns] - the human readable error message as string.
	String^ Fabric::GetErrorText(DWORD dw)
	{
		LPVOID lpMsgBuf;

		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			dw,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR) &lpMsgBuf,
			0, NULL );

		String^ result = Marshal::PtrToStringAuto (IntPtr::IntPtr (lpMsgBuf));

		LocalFree(lpMsgBuf);

		return result;

	}


}